/*------------------------------------------------------------------------------
 * @file    GREEDY_SNAKE.C
 * @author  嵌入式王子
 * @email   donminedm@163.com
 * @date    2024/12/22 21:42:09
 * @brief   开源的嵌入式软件包，本人不对此代码负责任
 * -----------------------------------------------------------------------------*/

#include "greedy_snake.h"
#include "log.h"

static snake_food __food__;
static snake __snake__;
static u8g2_t *__oled__;

// 蛇头宏定义
#define SNAKE_FONT __snake__.coor[0]

// 显示标题和倒计时
static void __show_titl_countdown(const char *titl, uint8_t countdown)
{
    char _countdown_char[3] = {0, 0, 0};
    uint8_t _countdown = countdown;
    // 清空屏幕
    u8g2_ClearDisplay(__oled__);
    // 显示标题
    u8g2_DrawStr(__oled__, 20, MAP_Y_DOWN_EDGE / 2, titl);
    // 显示倒计时
    do
    {
        sprintf(_countdown_char, "%d", _countdown);
        u8g2_DrawStr(__oled__, MAP_X_RIGHT_EDGE / 2, MAP_Y_DOWN_EDGE / 5 * 4, _countdown_char);
        u8g2_SendBuffer(__oled__);
        HAL_Delay(1000);
    } while (_countdown--);
}

static void __game_init()
{
    uint16_t i;
    // 食物默认初始化中心
    __food__.point.x = MAP_X_RIGHT_EDGE / 2;
    __food__.point.y = MAP_Y_DOWN_EDGE / 2;
    __food__.enable = 1;
    // 贪吃蛇初始化
    __snake__.length = SNAKE_DEFALUT_LEN;
    __snake__.dir = DIR_UP;
    __snake__.speed = SNAKE_DEFALUT_SPEED;
    memset(__snake__.coor, 0x00, sizeof(__snake__.coor));
    // 蛇默认从底部竖型创建
    SNAKE_FONT.x = MAP_X_RIGHT_EDGE / 2;
    SNAKE_FONT.y = MAP_Y_DOWN_EDGE - SNAKE_DEFALUT_LEN;
    for (i = 0; i < __snake__.length; i++)
    {
        __snake__.coor[i].x = SNAKE_FONT.x;
        __snake__.coor[i].y = SNAKE_FONT.y + i;
    }
    // 清空屏幕
    u8g2_ClearDisplay(__oled__);
    logi("snake game init...");
}

// 初始化游戏
void snake_game_init(u8g2_t *oled)
{
    // 屏幕对象初始化
    __oled__ = oled;
    if (__oled__ == NULL)
        return;
    __show_titl_countdown("Greedy Snake", 3);
    __game_init();
    logi("snake game start...");
}

// 游戏界面绘制
static void __update_ui()
{
    // 清空屏幕
    u8g2_ClearBuffer(__oled__);
    // 绘制围墙
    u8g2_DrawFrame(__oled__, MAP_X_LEFT_EDGE, MAP_Y_UP_EDGE, MAP_X_RIGHT_EDGE, MAP_Y_DOWN_EDGE);
    // 绘制蛇身
    for (uint16_t i = 0; i < __snake__.length; i++)
        u8g2_DrawPixel(__oled__, __snake__.coor[i].x, __snake__.coor[i].y);
    // 绘制食物
    u8g2_DrawPixel(__oled__, __food__.point.x, __food__.point.y);
    // 开始绘制
    u8g2_SendBuffer(__oled__);
}

void __game_over(uint8_t success)
{
    char msg[32] = {0x00};
    if (success)
    {
        logi("game success...");
        __show_titl_countdown("* Pass *", 3);
    }
    else
    {
        logi("game failure...");
        sprintf(msg, "You Grade:%d", __snake__.length - SNAKE_DEFALUT_LEN);
        __show_titl_countdown(msg, 5);
    }
    // 重新开始游戏
    __game_init();
}

// 撞墙死亡
static void __kill_knock()
{
    if ((SNAKE_FONT.x == MAP_X_LEFT_EDGE) || (SNAKE_FONT.x == MAP_X_RIGHT_EDGE) ||
        (SNAKE_FONT.y == MAP_Y_UP_EDGE || SNAKE_FONT.y == MAP_Y_DOWN_EDGE))
    {
        logw("snake knock...");
        __game_over(0);
    }
}

// 吃自己死亡
static void __kill_self()
{
    uint16_t i;
    for (i = __snake__.length - 1; i > 0; i--)
    {
        if ((SNAKE_FONT.y == __snake__.coor[i].y) && (SNAKE_FONT.x == __snake__.coor[i].x))
        {
            logw("snake eat self...");
            __game_over(0);
        }
    }
}

static void __cacl_font()
{
    switch (__snake__.dir)
    {
    case DIR_UP:
        SNAKE_FONT.y--;
        break;
    case DIR_DOWN:
        SNAKE_FONT.y++;
        break;
    case DIR_LEFT:
        SNAKE_FONT.x--;
        break;
    case DIR_RIGHT:
        SNAKE_FONT.x++;
        break;
    default:
        break;
    }
    // logd("snake next font -> [%d,%d]...", SNAKE_FONT.x, SNAKE_FONT.y);
}

// 贪吃蛇移动
static void __moving()
{
    // 将蛇身坐标往前移动
    uint16_t i;
    for (i = __snake__.length - 1; i > 0; i--)
        __snake__.coor[i] = __snake__.coor[i - 1];
    // 计算蛇头下一次位置
    __cacl_font();
    // 撞墙死亡判断
    __kill_knock();
    // 吃自己死亡判断
    __kill_self();
}

// 方向控制
static void __dir_control(snake_dir pin)
{
    uint8_t changed = 0;
    if (__snake__.dir != pin) // 相反键位屏蔽
    {
        if (pin == DIR_DOWN && __snake__.dir != DIR_UP)
            changed = 1;
        if (pin == DIR_UP && __snake__.dir != DIR_DOWN)
            changed = 1;
        if (pin == DIR_LEFT && __snake__.dir != DIR_RIGHT)
            changed = 1;
        if (pin == DIR_RIGHT && __snake__.dir != DIR_LEFT)
            changed = 1;
    }
    if (changed)
    {
        __snake__.dir = pin;
        logd("change direction -> %#x", pin);
    }
}

// 生成新的食物
void __create_new_food()
{
    uint8_t i;
    uint32_t random;
    uint8_t success = 0;
    snake_food new_food = {.enable = 1, .point = {0, 0}};
    while (!success) // 必须产生新的食物
    {
        // 通过时间产生在地图内的随机数
        random = HAL_GetTick() % MAP_X_RIGHT_EDGE;
        new_food.point.x = random;
        HAL_Delay(random % 5);
        random = HAL_GetTick() % MAP_Y_DOWN_EDGE;
        new_food.point.y = random;
        success = 1;
        // 生成的食物坐标不应该在边界范围内
        if (new_food.point.x < (MAP_X_LEFT_EDGE + 5) || new_food.point.x > (MAP_X_RIGHT_EDGE - 5) ||
            new_food.point.y < (MAP_Y_UP_EDGE + 5) || new_food.point.y > (MAP_Y_DOWN_EDGE - 5))
        {
            success = 0;
            logw("font create in the wall");
            continue;
        }
        // 生成的食物坐标不应该在蛇身
        for (i = 0; i < __snake__.length; i++)
            if (memcmp(&new_food.point, &__snake__.coor[i], sizeof(snake_food)) == 0)
            {
                logw("font create in the snake");
                success = 0;
                break;
            }
    }
    logd("new food point -> [%d,%d]", new_food.point.x, new_food.point.y);
    memcpy(&__food__, &new_food, sizeof(snake_food));
}

// 更新时间
static void __speed()
{
    if (__snake__.speed > SNAKE_MAX_SPEED)
    {
        __snake__.speed -= SNAKE_SPEED_UNIT; // 加快速度，减少延时时间
        logd("new speed -> %d", __snake__.speed);
    }
}

// 扔食物 吃食物
void __eat()
{
    if (__food__.enable) // 食物有效，未被吃
    {
        if (SNAKE_FONT.x == __food__.point.x && SNAKE_FONT.y == __food__.point.y) // 被吃
        {
            __food__.enable = 0;
            __snake__.length++;
            if (__snake__.length == SNAKE_MAX_LENGTH) // 通过
                __game_over(1);
            else // 未通过，新增食物，加快蛇速度
            {
                logd("update length -> %d", __snake__.length);
                __speed();           // 更新射速
                __create_new_food(); // 创建新的点
            }
        }
    }
}

void snake_game_loop()
{
    __update_ui();              // 绘制ui
    __moving();                 // 移动身体
    __eat();                    // 判断是否吃到食物
    HAL_Delay(__snake__.speed); // 通过延时控制蛇的前进速度
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    __dir_control(GPIO_Pin); // 通过方向键外部中断修改蛇的运行方向
}